﻿#include "stdafx.h"
#include "qg/qgasv.h"
#include "qg_trace.h"

//////////////////////////////////////////////////////////////////////////
// 디바이스 컨테이너

//
qgDevContainer::qgDevContainer()
: cnt(0), acc(0), ldn(0)
, roam(QGROAM_NONE)
{
	rasz.Initialize(qgRasz::Type());
	blnd.Initialize(qgBlnd::Type());
	splr.Initialize(qgSplr::Type());
	tex.Initialize(qgTexture::Type());
	shd.Initialize(qgShader::Type());
	los.Initialize(qgLayout::Type());
	dbs.Initialize();
	evs.Initialize();
	obj.Initialize();
	sub.Initialize();
	node.Initialize();
}

//
qgDevContainer::~qgDevContainer()
{
}

//
void qgDevContainer::Dispose(bool waitforloader)
{
	// 로딩 중인 애들 기둘림
	if (waitforloader)
	{
		for (kint i = 0;; i++)
		{
			if (k_atomget_ptr(&ldn) == 0)
				break;

			k_yield(i);
		}
	}

	// 본격 삭제
	tpl::BlueStr<2048> bs;

	if (node.IsHave())	// 노드 (OBJ_3)
	{
		bs.AppendFormat(", NODE[%d]", node.Count());
		roam = QGROAM_OBJ_3;
	}
	node.Dispose();

	if (sub.IsHave())	// 서브노드 (OBJ_2)
	{
		bs.AppendFormat(", SUB[%d]", sub.Count());
		roam = QGROAM_OBJ_2;
	}
	sub.Dispose();

	if (obj.IsHave())	// 오브젝트 (OBJ_1)
	{
		bs.AppendFormat(", OBJ[%d]", obj.Count());
		roam = QGROAM_OBJ_1;
	}
	obj.Dispose();

	// 재질 (MTL_3)

	if (shd.IsHave())	// 세이더 (MTL_2)
	{
		bs.AppendFormat(", SHADER[%d]", shd.Count());
		roam = QGROAM_MTL_2;
	}
	shd.Dispose();

	// 속성 (MTL_1)

	if (evs.IsHave())	// 이벤트 (EVENT)
	{
		bs.AppendFormat(", EVENT[%d]", evs.Count());
		roam = QGROAM_EVENT;
	}
	evs.Dispose();

	if (tex.IsHave())	// 텍슷차 (RES_5)
	{
		bs.AppendFormat(", TEX[%d]", tex.Count());
		roam = QGROAM_RES_5;
	}
	tex.Dispose();

	if (dbs.IsHave())	// 뎁스 버퍼 (RES_6)
	{
		bs.AppendFormat(", DEPTH[%d]", dbs.Count());
		roam = QGROAM_RES_6;
	}
	dbs.Dispose();

	if (los.IsHave())	// 레이아웃 (RES_4)
	{
		bs.AppendFormat(", LAYOUT[%d]", los.Count());
		roam = QGROAM_RES_4;
	}
	los.Dispose();

	if (splr.IsHave())	// 샘플러 (RES_3)
	{
		bs.AppendFormat(", SPLR[%d]", splr.Count());
		roam = QGROAM_RES_3;
	}
	splr.Dispose();

	if (blnd.IsHave())	// 블랜드 (RES_2)
	{
		bs.AppendFormat(", BLND[%d]", blnd.Count());
		roam = QGROAM_RES_2;
	}
	blnd.Dispose();

	if (rasz.IsHave())	// 래스터라이저 (RES_1)
	{
		bs.AppendFormat(", RASZ[%d]", rasz.Count());
		roam = QGROAM_RES_1;
	}
	rasz.Dispose();

	// 출력 해봄
	if (bs.IsHave())
	{
		kTsb type(qgDevice::Type());
		k_tracef(KLV_WARNING, type.GetName(), "ROAM LEFT: %s\n", bs.Data() + 2);
	}
}

//
bool qgDevContainer::Add(qgRoam r, qgObj* n)
{
	switch (r)
	{
		case QGROAM_RES_1:
			rasz.Add(n);
			break;

		case QGROAM_RES_2:
			blnd.Add(n);
			break;

		case QGROAM_RES_3:
			splr.Add(n);
			break;

		case QGROAM_RES_4:
			los.Add(n);
			break;

		case QGROAM_RES_5:
			tex.Add(n);
			break;

		case QGROAM_RES_6:
			dbs.Append(n);
			break;

		case QGROAM_MTL_2:
			shd.Add(n);
			break;

		case QGROAM_EVENT:
			evs.Append(n);
			break;

		case QGROAM_OBJ_1:
			obj.Append(n);
			break;

		case QGROAM_OBJ_2:
			sub.Append(n);
			break;

		case QGROAM_OBJ_3:
			node.Append(n);
			break;

		default:
			// 맞는 로암이 없음
			k_tracep(KLV_FATAL, kTsb(qgDevice::Type()).GetName(), KERR_NOTYPE);

			if (k_candebug())
				k_breakpoint();

			return false;
	}

	k_atominc_ptr(&cnt);
	k_atominc_ptr(&acc);

	return true;
}

//
bool qgDevContainer::Remove(qgRoam r, qgObj* n)
{
	switch (r)
	{
		case QGROAM_RES_1:
			rasz.RemoveNode(n);
			break;

		case QGROAM_RES_2:
			blnd.RemoveNode(n);
			break;

		case QGROAM_RES_3:
			splr.RemoveNode(n);
			break;

		case QGROAM_RES_4:
			los.RemoveNode(n);
			break;

		case QGROAM_RES_5:
			tex.RemoveNode(n);
			break;

		case QGROAM_RES_6:
			dbs.Remove(n);
			break;

		case QGROAM_MTL_2:
			shd.RemoveNode(n);
			break;

		case QGROAM_EVENT:
			evs.Remove(n);
			break;

		case QGROAM_OBJ_1:
			obj.Remove(n);
			break;

		case QGROAM_OBJ_2:
			sub.Remove(n);
			break;

		case QGROAM_OBJ_3:
			node.Remove(n);
			break;

		default:
			// 맞는 로암이 없음
			k_tracep(KLV_FATAL, kTsb(qgDevice::Type()).GetName(), KERR_NOTYPE);

			if (k_candebug())
				k_breakpoint();

			return false;
	}

	k_atomdec_ptr(&cnt);

	return true;
}

//
namespace _dev
{
	// 뎁스버퍼 찾기
	qgObj* DevCtnrFindDepthBuffer(kObjList& l, const tpl::Point* pt)
	{
		return (qgObj*)l.Find([ pt ](kObjNode* p) -> bool
		{
			qgDepthBuffer* d = (qgDepthBuffer*)p;
			return d->GetWidth() == pt->width && d->GetHeight() == pt->height;
		});
	}
}

//
qgObj* qgDevContainer::Find(qgRoam r, kconstpointer k)
{
	qgObj* n;

	switch (r)
	{
		case QGROAM_RES_1:
			n = (qgObj*)rasz.Get(k);
			break;

		case QGROAM_RES_2:
			n = (qgObj*)blnd.Get(k);
			break;

		case QGROAM_RES_3:
			n = (qgObj*)splr.Get(k);
			break;

		case QGROAM_RES_4:
			n = (qgObj*)los.Get(k);
			break;

		case QGROAM_RES_5:
			n = (qgObj*)tex.Get(k);
			break;

		case QGROAM_RES_6:
			n = _dev::DevCtnrFindDepthBuffer(dbs, (const tpl::Point*)k);
			break;

		case QGROAM_MTL_2:
			n = (qgObj*)shd.Get(k);
			break;

		default:
			n = NULL;
			break;
	}

	return n;
}

namespace _dev
{
	struct DevCtnrRoamSignalInvoke
	{
		const char* signal;
		kpointer data;

		DevCtnrRoamSignalInvoke(const char* signal, kpointer data)
			: signal(signal)
			, data(data)
		{
		}

		void operator()(kObjNode* p)
		{
			p->Signal(signal, data);
		}
	};
}

//
void qgDevContainer::Signal(const char* signal, kpointer argdata)
{
	_dev::DevCtnrRoamSignalInvoke invoker(signal, argdata);

	// 오브젝트 QGROAM_OBJ_1
	obj.LoopEach(invoker);

	// 이벤트 QGROAM_EVENT
	evs.LoopEach(invoker);

	// 뎁스 QGROAM_RES_6
	dbs.LoopEach(invoker);

	// 텍스쳐 QGROAM_RES_5
	tex.LoopEach(invoker);
}
